#include "actor_system.h"
#include "engine/renderer/mesh/particle_system.h"

namespace kanon
{
    Actor& ActorSystem::CreateSprite()
    {
        return this->CreateSprite(glm::vec2(0.0, 0.0), glm::vec2(1.0, 1.0), glm::vec4(1.0f, 1.0f, 1.0f, 1.0f));
    }

    Actor& ActorSystem::CreateSprite(const glm::vec2 pos, const glm::vec2 size, const std::shared_ptr<Texture2D>& texture)
    {
        auto actor = std::make_unique<Actor>(next_actor_id);
        next_actor_id += 1;
        actor->AddComponent<SpriteMeshComponent>(size);
        actor->AddComponent<Transform2DComponent>(pos, 0.0f, 1.0f);
        actor->AddComponent<Render2DComponent>(texture);
        actor->AddComponent<Collider2DComponent>(glm::vec2(0.0f, 0.0f), size);

        auto& ref = *actor;
        actors.push_back(std::move(actor));
        return ref;
    }

    Actor& ActorSystem::CreateSprite(const glm::vec2 pos, const glm::vec2 size, const glm::vec4 color)
    {
        auto actor = std::make_unique<Actor>(next_actor_id);
        next_actor_id += 1;
        actor->AddComponent<SpriteMeshComponent>(size);
        actor->AddComponent<Transform2DComponent>(pos, 0.0f, 1.0f);
        actor->AddComponent<Render2DComponent>(color);
        actor->AddComponent<Collider2DComponent>(glm::vec2(0.0f, 0.0f), size);

        auto& ref = *actor;
        actors.push_back(std::move(actor));
        return ref;
    }

    Actor& ActorSystem::CreateParticleSystem(const glm::vec2& pos, const glm::vec4& color, uint32_t num_particles)
    {
        auto actor = std::make_unique<Actor>(next_actor_id);
        next_actor_id += 1;
        actor->AddComponent<Transform2DComponent>(pos, 0.0f, 1.0f);
        actor->AddComponent<ParticleMeshComponent>(num_particles);
        actor->AddComponent<ParticleRenderComponent>(color);

        auto& ref = *actor;
        actors.push_back(std::move(actor));
        return ref;
    }

    Actor& ActorSystem::CreateCircle(const glm::vec2 pos, const glm::vec2 radius, const glm::vec4 color)
    {
        auto actor = std::make_unique<Actor>(next_actor_id);
        next_actor_id += 1;
        actor->AddComponent<CircleMeshComponent>(radius);
        actor->AddComponent<Transform2DComponent>(pos, 0.0f, 1.0f);
        actor->AddComponent<CircleRenderComponent>(color);
        actor->AddComponent<Collider2DComponent>(-radius, radius);

        auto& ref = *actor;
        actors.push_back(std::move(actor));
        return ref;
    }

    void ActorSystem::Update(float deltaTime)
    {
        for (auto& actor : actors)
        {
            if (actor->HasComponent<Physics2DComponent>())
            {
                auto& physics = actor->GetComponent<Physics2DComponent>();
                physics.Update(deltaTime);
            }

            if (actor->HasComponent<ParticleMeshComponent>())
            {
                auto& particles = actor->GetComponent<ParticleMeshComponent>();
                particles.Update(deltaTime);
            }
        }
    }

    int32_t ActorSystem::Pick(const glm::vec2& position)
    {
        int index = 0;
        // TODO: iterate in reversed order.
        for (auto& actor : actors)
        {
            if (!actor->HasComponent<Collider2DComponent>())
                continue;
            auto& c = actor->GetComponent<Collider2DComponent>();

            // check overlap
            if (pointInBox(position, c.bbox))
                return index;
            index += 1;
        }

        return -1;
    }

    bool ActorSystem::pointInBox(const glm::vec2& pt, const glm::vec4& box)
    {
        // box vec4 in (x1, y1, x2, y2)
        if (pt.x > box.b || pt.x < box.r)
            return false;
        if (pt.y > box.a || pt.y < box.g)
            return false;
        return true;
    }
}
